package cn.anecansaitin.whimsy.common.item;

import cn.anecansaitin.whimsy.client.item.render.RockSceptreRender;
import cn.anecansaitin.whimsy.common.capabilities.IComboHandler;
import cn.anecansaitin.whimsy.common.network.RockSceptreKeyInput;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.animatable.GeoItem;
import software.bernie.geckolib.animatable.SingletonGeoAnimatable;
import software.bernie.geckolib.core.animatable.GeoAnimatable;
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.core.animation.AnimatableManager;
import software.bernie.geckolib.core.animation.AnimationController;
import software.bernie.geckolib.core.animation.RawAnimation;
import software.bernie.geckolib.core.object.PlayState;
import software.bernie.geckolib.util.GeckoLibUtil;

import java.util.function.Consumer;

public class RockSceptre extends Item implements GeoItem {
    private final Multimap<Attribute, AttributeModifier> defaultModifiers;
    private static final RawAnimation idle = RawAnimation.begin()
            .thenPlay("idle");
    private static final RawAnimation attack1Precast = RawAnimation.begin()
            .thenPlayAndHold("attack1.precast");
    private static final RawAnimation attack1Active = RawAnimation.begin()
            .thenPlayAndHold("attack1.active");
    private static final RawAnimation attack2Precast = RawAnimation.begin()
            .thenPlayAndHold("attack2.precast");
    private static final RawAnimation attack2Active = RawAnimation.begin()
            .thenPlayAndHold("attack2.active");
    private static final RawAnimation attack3Precast = RawAnimation.begin()
            .thenPlayAndHold("attack3.precast");
    private static final RawAnimation attack3Active = RawAnimation.begin()
            .thenPlayAndHold("attack3.active");
    private static final RawAnimation attack4Charging = RawAnimation.begin()
            .thenPlayAndHold("attack4.charging");
    private static final RawAnimation attack4Backswing = RawAnimation.begin()
            .thenPlay("attack4.backswing");
    private static final RawAnimation attack5Precast = RawAnimation.begin()
            .thenPlayAndHold("attack5.precast");
    private static final RawAnimation attack5Active = RawAnimation.begin()
            .thenPlayAndHold("attack5.active");
    private static final RawAnimation attack5Backswing = RawAnimation.begin()
            .thenPlayAndHold("attack5.backswing");
    private static final RawAnimation attack6 = RawAnimation.begin()
            .thenPlayAndHold("attack6");
    private static final RawAnimation attack7 = RawAnimation.begin()
            .thenPlayAndHold("attack7");
    private static final RawAnimation projectileShow0 = RawAnimation.begin()
            .thenPlayAndHold("projectile.show0");
    private static final RawAnimation projectileShow1 = RawAnimation.begin()
            .thenPlayAndHold("projectile.show1");
    private static final RawAnimation projectileShow2 = RawAnimation.begin()
            .thenPlayAndHold("projectile.show2");
    private static final RawAnimation projectileShow3 = RawAnimation.begin()
            .thenPlayAndHold("projectile.show3");
    private static final RawAnimation projectileShow4 = RawAnimation.begin()
            .thenPlayAndHold("projectile.show4");
    private static final RawAnimation projectileHide0 = RawAnimation.begin()
            .thenPlayAndHold("projectile.hide0");
    private static final RawAnimation projectileHide1 = RawAnimation.begin()
            .thenPlayAndHold("projectile.hide1");
    private static final RawAnimation projectileHide2 = RawAnimation.begin()
            .thenPlayAndHold("projectile.hide2");
    private static final RawAnimation projectileHide3 = RawAnimation.begin()
            .thenPlayAndHold("projectile.hide3");
    private static final RawAnimation projectileHide4 = RawAnimation.begin()
            .thenPlayAndHold("projectile.hide4");
    private static final RawAnimation stoneIdle = RawAnimation.begin()
            .thenPlay("stone.idle");
    private static final RawAnimation stoneAttack = RawAnimation.begin()
            .thenPlay("stone.attack");
    private static final RawAnimation stoneCharging = RawAnimation.begin()
            .thenPlay("stone.charging");
    private static final RawAnimation stoneEarthquake = RawAnimation.begin()
            .thenPlay("stone.earthquake");
    private static final RawAnimation stoneLoopEarthquake = RawAnimation.begin()
            .thenLoop("stone.earthquake");
    private static final RawAnimation stoneStoneSpike = RawAnimation.begin()
            .thenPlay("stone.stone_spike");

    private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);

    public RockSceptre() {
        super(new Properties().rarity(Rarity.EPIC));
        SingletonGeoAnimatable.registerSyncedAnimatable(this);
        ImmutableMultimap.Builder<Attribute, AttributeModifier> builder = ImmutableMultimap.builder();
        builder.put(Attributes.ATTACK_SPEED, new AttributeModifier(BASE_ATTACK_SPEED_UUID, "Weapon modifier", 100d, AttributeModifier.Operation.ADDITION));
        defaultModifiers = builder.build();
    }

    @Override
    public Multimap<Attribute, AttributeModifier> getAttributeModifiers(EquipmentSlot slot, ItemStack stack) {
        return slot == EquipmentSlot.MAINHAND ? defaultModifiers : ImmutableMultimap.of();
    }

    @Override
    public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
        controllers.add(new AnimationController<GeoAnimatable>(this, "con", 0, (i) -> {
            if (i.getController().getAnimationState() == AnimationController.State.STOPPED) {
                return PlayState.STOP;
            }

            return PlayState.CONTINUE;
        })
                .triggerableAnim("idle", idle)
                .triggerableAnim("attack1.precast", attack1Precast)
                .triggerableAnim("attack1.active", attack1Active)
                .triggerableAnim("attack2.precast", attack2Precast)
                .triggerableAnim("attack2.active", attack2Active)
                .triggerableAnim("attack3.precast", attack3Precast)
                .triggerableAnim("attack3.active", attack3Active)
                .triggerableAnim("attack4.charging", attack4Charging)
                .triggerableAnim("attack4.backswing", attack4Backswing)
                .triggerableAnim("attack5.precast", attack5Precast)
                .triggerableAnim("attack5.active", attack5Active)
                .triggerableAnim("attack5.backswing", attack5Backswing)
                .triggerableAnim("attack6", attack6)
                .triggerableAnim("attack7", attack7));

        controllers.add(new AnimationController<GeoAnimatable>(this, "part0", 0, i -> i.setAndContinue(projectileHide0)).triggerableAnim("show", projectileShow0).triggerableAnim("hide", projectileHide0));
        controllers.add(new AnimationController<GeoAnimatable>(this, "part1", 0, i -> i.setAndContinue(projectileHide1)).triggerableAnim("show", projectileShow1).triggerableAnim("hide", projectileHide1));
        controllers.add(new AnimationController<GeoAnimatable>(this, "part2", 0, i -> i.setAndContinue(projectileHide2)).triggerableAnim("show", projectileShow2).triggerableAnim("hide", projectileHide2));
        controllers.add(new AnimationController<GeoAnimatable>(this, "part3", 0, i -> i.setAndContinue(projectileHide3)).triggerableAnim("show", projectileShow3).triggerableAnim("hide", projectileHide3));
        controllers.add(new AnimationController<GeoAnimatable>(this, "part4", 0, i -> i.setAndContinue(projectileHide4)).triggerableAnim("show", projectileShow4).triggerableAnim("hide", projectileHide4));
        controllers.add(new AnimationController<GeoAnimatable>(this, "stone", 0, i -> i.setAndContinue(stoneIdle))
                .triggerableAnim("charging", stoneCharging)
                .triggerableAnim("idle", stoneIdle)
                .triggerableAnim("attack", stoneAttack)
                .triggerableAnim("earthquake", stoneEarthquake)
                .triggerableAnim("earthquake.loop", stoneLoopEarthquake)
                .triggerableAnim("stone_spike", stoneStoneSpike));
    }

    @Override
    public AnimatableInstanceCache getAnimatableInstanceCache() {
        return geoCache;
    }

    @Override
    public InteractionResult useOn(UseOnContext pContext) {
        return InteractionResult.PASS;
    }

    @Override
    public void initializeClient(Consumer<IClientItemExtensions> consumer) {
        consumer.accept(new IClientItemExtensions() {
            @Nullable
            private RockSceptreRender render;

            @Override
            public BlockEntityWithoutLevelRenderer getCustomRenderer() {
                if (render == null) {
                    render = new RockSceptreRender();
                }

                return render;
            }
        });
    }

    @Override
    public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
        player.startUsingItem(hand);
        return InteractionResultHolder.success(player.getItemInHand(hand));
    }

    @Override
    public void onUseTick(Level level, LivingEntity livingEntity, ItemStack stack, int pRemainingUseDuration) {
        if (!level.isClientSide()) { //todo 改为使用keyInput事件来处理右键
            IComboHandler comboHandler = stack.getCapability(IComboHandler.COMBO);

            if (comboHandler == null) {
                return;
            }

            comboHandler.keyInput(2);
        }
    }

    @Override
    public int getUseDuration(ItemStack pStack) {
        return 72000;
    }

    @Override
    public boolean onLeftClickEntity(ItemStack stack, Player player, Entity entity) {
        return true;
    }

    @Override
    public boolean canAttackBlock(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer) {
        return false;
    }

    @Override
    public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
        return false;
    }

    @Override
    public boolean onEntitySwing(ItemStack stack, LivingEntity entity) {
        return true;
    }

    @Override
    public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotId, boolean isSelected) {
        if (!level.isClientSide && entity instanceof ServerPlayer player) {
            IComboHandler comboHandler = stack.getCapability(IComboHandler.COMBO);

            if (comboHandler != null) {
                comboHandler.comboTick((Player) entity);
            }
        }
    }

    public void onKeyInput(ItemStack stack, RockSceptreKeyInput input) {
        IComboHandler comboHandler = stack.getCapability(IComboHandler.COMBO);

        if (comboHandler != null) {
            comboHandler.keyInput(1);
        }
    }
}
